#include "stdafx.h"


#include <winsock2.h>

struct TPing
{
	int m_Loop;
	int m_Error;
	int m_AllTime;
};

int icmp_test(const char *addr, const char *parm, char *rOut, TPing &aPing);	//add byFun 20100622

#include <windows.h>

typedef HANDLE WINAPI TIcmpCreateFile(VOID); /* INVALID_HANDLE_VALUE on error */
typedef BOOL WINAPI TIcmpCloseHandle(HANDLE IcmpHandle); /* FALSE on error */

typedef struct ip_option_information {
    u_char Ttl;		/* Time To Live (used for traceroute) */
    u_char Tos; 	/* Type Of Service (usually 0) */
    u_char Flags; 	/* IP header flags (usually 0) */
    u_char OptionsSize; /* Size of options data (usually 0, max 40) */
    u_char FAR *OptionsData;   /* Options data buffer */
} IPINFO, *PIPINFO, FAR *LPIPINFO;

typedef struct icmp_echo_reply {
    u_long Address; 	/* source address *.
    u_long Status;	/* IP status value (see below) */
    u_long RTTime;	/* Round Trip Time in milliseconds */
    u_short DataSize; 	/* reply data size */
    u_short Reserved; 	/* */
    void FAR *Data; 	/* reply data buffer */
    struct ip_option_information Options; /* reply options */
} ICMPECHO, *PICMPECHO, FAR *LPICMPECHO;


typedef DWORD WINAPI TIcmpSendEcho(
    HANDLE IcmpHandle, 	/* handle returned from IcmpCreateFile() */
    u_long DestAddress, /* destination IP address (in network order) */
    LPVOID RequestData, /* pointer to buffer to send */
    WORD RequestSize,	/* length of data in buffer */
    LPIPINFO RequestOptns,  /* see Note 2 */
    LPVOID ReplyBuffer, /* see Note 1 */
    DWORD ReplySize, 	/* length of reply (must allow at least 1 reply) */
    DWORD Timeout 	/* time in milliseconds to wait for reply */
);


#define IP_STATUS_BASE 11000
#define IP_SUCCESS 0
#define IP_BUF_TOO_SMALL (IP_STATUS_BASE + 1)
#define IP_DEST_NET_UNREACHABLE (IP_STATUS_BASE + 2)
#define IP_DEST_HOST_UNREACHABLE (IP_STATUS_BASE + 3)
#define IP_DEST_PROT_UNREACHABLE (IP_STATUS_BASE + 4)
#define IP_DEST_PORT_UNREACHABLE (IP_STATUS_BASE + 5)
#define IP_NO_RESOURCES (IP_STATUS_BASE + 6)
#define IP_BAD_OPTION (IP_STATUS_BASE + 7)
#define IP_HW_ERROR (IP_STATUS_BASE + 8)
#define IP_PACKET_TOO_BIG (IP_STATUS_BASE + 9)
#define IP_REQ_TIMED_OUT (IP_STATUS_BASE + 10)
#define IP_BAD_REQ (IP_STATUS_BASE + 11)
#define IP_BAD_ROUTE (IP_STATUS_BASE + 12)
#define IP_TTL_EXPIRED_TRANSIT (IP_STATUS_BASE + 13)
#define IP_TTL_EXPIRED_REASSEM (IP_STATUS_BASE + 14)
#define IP_PARAM_PROBLEM (IP_STATUS_BASE + 15)
#define IP_SOURCE_QUENCH (IP_STATUS_BASE + 16)
#define IP_OPTION_TOO_BIG (IP_STATUS_BASE + 17)
#define IP_BAD_DESTINATION (IP_STATUS_BASE + 18)
#define IP_ADDR_DELETED (IP_STATUS_BASE + 19)
#define IP_SPEC_MTU_CHANGE (IP_STATUS_BASE + 20)
#define IP_MTU_CHANGE (IP_STATUS_BASE + 21)
#define IP_UNLOAD (IP_STATUS_BASE + 22)
#define IP_GENERAL_FAILURE (IP_STATUS_BASE + 50)
#define MAX_IP_STATUS IP_GENERAL_FAILURE
#define IP_PENDING (IP_STATUS_BASE + 255)

#define BUFSIZE     8192
#define DEFAULT_LEN 32
#define LOOPLIMIT   8
#define TIMEOUT     5000
#define DEFAULT_TTL 64

#define IPFLAG_DONT_FRAGMENT 0x02

#define IPOPT_ROUTER_ALERT 0x94040000L

char achReqData[BUFSIZE];
char achRepData[sizeof(ICMPECHO) + BUFSIZE];

char achBCastAddr[] = {"255.255.255.255"};

TIcmpCreateFile* lpfnIcmpCreateFile;
TIcmpCloseHandle* lpfnIcmpCloseHandle;
TIcmpSendEcho* lpfnIcmpSendEcho;

/*int main(int argc, char* argv[]) {
	char *sOut = (char*)malloc(1024);
	if(sOut != NULL){
		memset(sOut, 0, 1024);
		icmp_test("www.google.com", "-t", sOut);
		printf(sOut);

		memset(sOut, 0, 1024);
		icmp_test("www.baidu.com", "-t", sOut);
		printf(sOut);

		memset(sOut, 0, 1024);
		icmp_test("www.google.com", "-c", sOut);
		printf(sOut);

		free(sOut);
		sOut = NULL;
	}else{
		printf("memory alloc fail");
	}
}*/

int icmp_test(const char *addr, const char *parm, char *rOut, TPing &aPing){
    WSADATA wsaData;
    IPINFO stIPInfo, *lpstIPInfo;
    u_long lIPOption;
    LPHOSTENT lpstHost = NULL;
    HANDLE hICMP;
	HMODULE hICMP_DLL;
    int nRet, i, j, k, nDataLen, nLoopLimit, nTimeOut, nTTL, nTOS;
    DWORD dwReplyCount;
    IN_ADDR stDestAddr;
    BOOL fRet, fFlag, fDontStop, fDontFrag, fDebug, fRouterAlert;
    BOOL fTraceRoute;
	char stmp[256]={0};

    nRet = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (nRet) {
        sprintf(rOut, "WSAStartup() failed, err: %d\n", nRet);
	    return -1;
    }
    
    if(addr != '\0'){
		u_long lAddr;
		lAddr = inet_addr(addr);
		if (lAddr == INADDR_NONE) {
			for (i=0;i<16;i++) {
				if (*(addr+i) != achBCastAddr[i]) {
					lAddr = 0L;
					break;
				}
			}
		}
		if(!lAddr){
			lpstHost = gethostbyname(addr);
			if (!lpstHost) {
				sprintf(rOut, "%s is an invalid address or hostname (err: %d)\n", parm, WSAGetLastError());
				WSACleanup();
				return -2;
			} else {
				stDestAddr.s_addr = *(u_long*)lpstHost->h_addr;
			}
		} else {
			stDestAddr.s_addr = lAddr;
		}
	}

    nDataLen   = DEFAULT_LEN;
    nLoopLimit = LOOPLIMIT;
    nTimeOut   = TIMEOUT;
    fDontStop  = FALSE;
    fDontFrag  = FALSE;
    lpstIPInfo = NULL;
    fDebug     = FALSE;
    nTTL       = DEFAULT_TTL;
    nTOS       = 0;
    fRouterAlert = FALSE;
    fTraceRoute  = FALSE;

	fFlag = FALSE;
     
	if(!((parm[0] == '-') || (parm[0] == '/'))) {
		return -3;
	}

	if (parm[1] == 'c') {
		//fDontStop = TRUE;
		sprintf(stmp, "Send continuous pings indefinately\n");
		strcat(rOut, stmp);
	}

	if (parm[1] == 't') {
		fTraceRoute = TRUE;
		sprintf(stmp, "TraceRoute enabled\n");
		strcat(rOut, stmp);
	}

    hICMP_DLL = LoadLibrary("ICMP.DLL");
    if (hICMP_DLL == 0) {
        printf ("LoadLibrary() failed: Unable to locate ICMP.DLL!\n");
        exit(3);
    }

    lpfnIcmpCreateFile  = (TIcmpCreateFile *)GetProcAddress(hICMP_DLL,"IcmpCreateFile");
    lpfnIcmpCloseHandle = (TIcmpCloseHandle *)GetProcAddress(hICMP_DLL,"IcmpCloseHandle");
    lpfnIcmpSendEcho    = (TIcmpSendEcho *)GetProcAddress(hICMP_DLL,"IcmpSendEcho");
    if ((!lpfnIcmpCreateFile) || (!lpfnIcmpCloseHandle) || (!lpfnIcmpSendEcho)) {
        sprintf(rOut, "GetProcAddr() failed for at least one function.\n");
        return -4;
    }

    hICMP = (HANDLE) lpfnIcmpCreateFile();
    if (hICMP == INVALID_HANDLE_VALUE) {
        sprintf(rOut, "IcmpCreateFile() failed, err: ");
		//PIcmpErr(GetLastError());
        return -5;
    }
   
    for (j=0, i=32; j<nDataLen; j++, i++) {
        if (i>=126) 
	    i= 32;
        achReqData[j] = i;
    }

    if (fTraceRoute) {
		fDontStop = TRUE;
		nTTL = 0;
    }

    lpstIPInfo = &stIPInfo;
    stIPInfo.Ttl      = nTTL;
    stIPInfo.Tos      = nTOS;
    stIPInfo.Flags    = fDontFrag ? IPFLAG_DONT_FRAGMENT : 0;
    if (fRouterAlert) {
      lIPOption  = htonl(IPOPT_ROUTER_ALERT);
      stIPInfo.OptionsSize = sizeof(lIPOption);
      stIPInfo.OptionsData = (u_char *)&lIPOption;
    } else {
      stIPInfo.OptionsSize = 0;
      stIPInfo.OptionsData = NULL;
    }
	    
    if (lpstHost) {
        sprintf(stmp, "ICMP Testing %s [%s] with %d bytes of data:\n", lpstHost->h_name, inet_ntoa(stDestAddr), nDataLen);
    } else {
		sprintf(stmp, "ICMP Testing %s with %d bytes of data:\n", inet_ntoa(stDestAddr), nDataLen);
    }
	strcat(rOut, stmp);
	if (!fTraceRoute)
	{
		aPing.m_Loop = nLoopLimit;
		aPing.m_AllTime = 0;
		aPing.m_Error = 0;
	}
	int aTraceRouteErrorTime = 0;
    for (k=0; k<nLoopLimit; k++) {
      if (k) SleepEx(100, TRUE);
      if (fDontStop && (k+1 == nLoopLimit)) k=0;
      
      if (fTraceRoute) {
         ++stIPInfo.Ttl;
      }

	dwReplyCount = lpfnIcmpSendEcho(hICMP, 
		stDestAddr.s_addr,
		achReqData,
		nDataLen,
		lpstIPInfo, 
		achRepData, 
		sizeof(achRepData), 
		nTimeOut);
	if (dwReplyCount != 0) {
		IN_ADDR stDestAddr;
		DWORD   dwStatus;
	
		stDestAddr.s_addr = *(u_long *)achRepData;
        sprintf(stmp, "Reply from %s: time=%ldms TTL=%u\n", 
			    inet_ntoa(stDestAddr), 
				*(u_long *) &(achRepData[8]),
				fTraceRoute ? stIPInfo.Ttl : (*(char *)&(achRepData[20]))&0x00FF);
		 strcat(rOut, stmp);
		 if (!fTraceRoute)
		 {
			 aPing.m_AllTime += *(u_long *) &(achRepData[8]);
		 }

		dwStatus = *(DWORD *) &(achRepData[4]);
		if (dwStatus != IP_SUCCESS) {
// 			if (!fTraceRoute){
// 				PIcmpErr(dwStatus);
// 			}
		} else if (fTraceRoute) {
			sprintf (stmp, "Traceroute complete: Destination is %d hops away\n\n\n", stIPInfo.Ttl);
			strcat(rOut, stmp);
			break;  /* We're done, since we reached the destination! */
		}

		} else {
			sprintf(stmp, "IcmpSendEcho() failed, err=%d", GetLastError());
			strcat(rOut, stmp);
			if (!fTraceRoute)
			{
				 aPing.m_Error++;
			}
			else
				aTraceRouteErrorTime++;
			if (aTraceRouteErrorTime > 4)
			{
				break;
			}
			//if (!fTraceRoute){
			//	break;  // end= loop
			//}
		}
    } 

    fRet = lpfnIcmpCloseHandle(hICMP);
    if (fRet == FALSE) {
		sprintf(stmp, "Error closing ICMP handle, err=%d", GetLastError());
		strcat(rOut, stmp);
    }

    // Shut down...
    FreeLibrary(hICMP_DLL);
    WSACleanup();
	if (aTraceRouteErrorTime > 4)
	{
		return -10;
	}
	return 1;
}